package com.lanmao.blog.filter;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lanmao.blog.entity.User;
import com.lanmao.blog.exception.CodeException;
import com.lanmao.blog.exception.PhoneErrorException;
import com.lanmao.blog.service.UserService;
import com.lanmao.blog.utils.BLOG_CONSTANT;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    // TODO: 2022/4/5 该参数可以抽取成配置，最后通过配置文件进行修改，这样作为共用组件只需要实现一个 default，具体值可以有调用者指定
    private String mobileParameter = "phone";
    private boolean postOnly = true;
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    UserService userService;
    /**
     * 通过构造函数指定该 Filter 要拦截的 url 和 httpMethod
     */
    @Override
    @Autowired
    public void setAuthenticationManager(AuthenticationManager authenticationManager) {
        super.setAuthenticationManager(authenticationManager);
    }
    public SmsCodeAuthenticationFilter() {
        super(new AntPathRequestMatcher("/phonelogin", "POST"));
    }
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        if (this.postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        } else {
            String mobile = obtainMobile(request);
            String code = obtainCode(request);
            mobile=mobile==null?"":mobile.trim();
            String redisinfo = stringRedisTemplate.opsForValue().get(BLOG_CONSTANT.PHONE_CACHE + mobile);
            User user = userService.getOne(new QueryWrapper<User>().eq("phone", mobile));
            if (user==null){
                throw  new PhoneErrorException("该用户不存在,请重新输入");
            }
            if (redisinfo==null||redisinfo.isEmpty()){
                throw  new CodeException("验证码过期,请重新获取") ;
            }
            String rediscode = redisinfo.split("_")[0];
            if (!rediscode.equals(code)) {
                throw  new CodeException("验证码错误,请重新获取") ;
            }
            stringRedisTemplate.delete(BLOG_CONSTANT.PHONE_CACHE+mobile);
            // 2. 封装成 Token 调用 AuthenticationManager 的 authenticated 方法，该方法中根据 Token 的类型去调用对应 Provider 的 authenticated
            SmsCodeAuthenticationToken token = new SmsCodeAuthenticationToken(mobile);
            this.setDetails(request, token);
            // 3. 返回 authenticated 方法的返回值
            return this.getAuthenticationManager().authenticate(token);
        }
    }
    protected String obtainMobile(HttpServletRequest request) {
        return request.getParameter(mobileParameter);
    }protected String obtainCode(HttpServletRequest request) {
        return request.getParameter("code");
    }
    protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) {
        authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
    }

    public String getMobileParameter() {
        return mobileParameter;
    }

    public void setMobileParameter(String mobileParameter) {
        Assert.hasText(mobileParameter, "mobileParameter parameter must not be empty or null");
        this.mobileParameter = mobileParameter;
    }

    public void setPostOnly(boolean postOnly) {
        this.postOnly = postOnly;
    }
}
